package entities;

import listener.PiecesListener;
import view.ChessBoard;

import javax.swing.*;
import java.awt.*;
import java.util.HashSet;
import java.util.Map;
import java.util.Set;
import java.util.concurrent.CopyOnWriteArraySet;


/**
 * Author: Mr.Zhao, Time: 2018/4/27/21:27
 * Description: 所有棋子的父类
 */
public abstract class BasePiece extends JLabel {
    /**
     * 棋子背景颜色
     */
    private static final Color BACKGROUND_COLOR = new Color(255, 200, 136);

    /* 黑棋 */
    public static final Color BLACK_PIECE = Color.BLACK;

    /* 红棋 */
    public static final Color RED_PIECE = Color.RED;

    /**
     * 字体背景颜色，也是用来区分敌我的标志
     */
    protected Color color;
    /**
     * 棋子所在位置
     */
    protected Point position;
    /**
     * 绘制棋子所需的直径，单位：像素
     */
    private int diameter;
    /**
     * 棋子名称
     */
    protected String pieceName;

    /**
     * 字体
     */
    //protected static final Font FONT = new Font("楷书", Font.BOLD, (int) (ChessBoard.CELL_HEIGHT * 0.4));
    private static Font FONT = null;

    private Set<PiecesListener> listeners = new HashSet<PiecesListener>();

    /* 移动之前的坐标点 */
    protected Point lastPosition;

    /* 可走的坐标点 */
    protected Set<AvailablePoint> availablePoints = new CopyOnWriteArraySet<>();

    /* 棋盘对象 */
    private ChessBoard chessBoard = null;

    /* 该棋子是否可移动棋子 */
    private boolean canMove = true;

    public BasePiece(int x, int y, Color color) {
        chessBoard = ChessBoard.getInstance();
        FONT = new Font("楷书", Font.BOLD, (int) (chessBoard.getCellHeight() * 0.4));
        /* 根据格子的长宽最小值计算所需直径 */
        int min = chessBoard.getCellHeight() < chessBoard.getCellWidth() ?
                chessBoard.getCellHeight() : chessBoard.getCellWidth();
        this.diameter = (int) (min * 0.38 * 2);

        this.setPosition(x, y);
        this.color = color;
    }

    /**
     * 添加监听器
     *
     * @param l
     * @author 赵永远
     */
    public void addPiecesListener(PiecesListener l) {
        if (l == null) {
            return;
        }
        this.listeners.add(l);
    }

    public void removePiecesListener(PiecesListener l) {
        if (this.listeners.contains(l)) {
            this.listeners.remove(l);
        }
    }

    @Override
    protected void paintComponent(Graphics g) {
        //System.out.println(this.pieceName + " paintComponent ");
        g.setColor(BACKGROUND_COLOR);
        int x = this.position.x;
        int y = this.position.y;
        g.fillOval(0, 0, diameter, diameter);
        g.setColor(color);
        g.setFont(FONT);
        g.drawString(this.pieceName, (int) (diameter * 0.24),
                (int) (diameter * 0.7));

    }

    /**
     * 根据当前棋盘所有棋子位置以及走法规则产生的可行坐标点
     *
     * @author 赵永远
     */
    abstract public void updateAvailablePoints(Map<Point, BasePiece> pointPieceMap);

    /**
     * 设置棋子位置，一般用于初始化棋盘时调用
     * TODO:将此方法与moveTo整合
     *
     * @param x
     * @param y
     * @author 赵永远
     */
    private void setPosition(int x, int y) {
        this.position = new Point(x, y);
        this.setBounds(chessBoard.getMargin() + x * chessBoard.getCellWidth() - diameter / 2,
                chessBoard.getMargin() + y * chessBoard.getCellHeight() - diameter / 2,
                diameter, diameter);
    }

    /**
     * 移动棋子
     *
     * @param x
     * @param y
     * @author 赵永远
     */
    public void moveTo(int x, int y) {
        for (PiecesListener listener : listeners) {
            listener.beforePieceMoved(this);
        }

        /* 能够移动才触发监听事件 */
        if (canMove) {
            this.lastPosition = this.position;

            this.position = new Point(x, y);
            /* 触发棋子的移动后事件，实现方法在controller中 */
            for (PiecesListener listener : listeners) {
                listener.afterPieceMoved(this);
            }
        }

    }

    public Point getPosition() {
        return this.position;
    }

    public Point getLastPosition() {
        return this.lastPosition;
    }

    public String getPieceName() {
        return this.pieceName;
    }

    public Color getColor() {
        return this.color;
    }

    public void setColor(Color color) {
        this.color = color;
    }

    public Set<AvailablePoint> getAvailablePoints() {
        return availablePoints;
    }

    public int getDiameter() {
        return this.diameter;
    }

}
